#include <gccore.h>
#include <ogcsys.h>
//#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <ogc/machine/processor.h>
#include "safe_vector.hpp"
#include <string>

#include "homebrew.h"
#include "dolloader.h"
#include "gecko.h"
#include "fs_utils.h"

static u8 *homebrewbuffer = (u8 *)0x92000000;
static u32 homebrewsize = 0;
static safe_vector<std::string> Arguments;

extern const u8 app_booter_dol[];
extern const u32 app_booter_dol_size;

bool bootHB = false;

void AddBootArgument(const char * argv)
{
	std::string arg(argv);
	Arguments.push_back(arg);
}

int CopyHomebrewMemory(SmartBuf temp, u32 pos, u32 len)
{
	homebrewsize += len;
    memcpy(homebrewbuffer+pos, temp.get(), len);
	DCFlushRange(homebrewbuffer+pos, len);

	return 1;
}

void FreeHomebrewBuffer()
{
	homebrewbuffer = (u8 *)0x92000000;
	homebrewsize = 0;
}

int LoadHomebrew(const char * filepath)
{
	SmartBuf buffer = FS_Read_File(filepath);
	return !!buffer ? CopyHomebrewMemory(buffer, 0, sizeof buffer) : 0;
}

static int SetupARGV(struct __argv * args)
{
	if(!args) return -1;

	bzero(args, sizeof(struct __argv));
	args->argvMagic = ARGV_MAGIC;

	u32 stringlength = 1;

	/** Append Arguments **/
	for(u32 i = 0; i < Arguments.size(); i++)
		stringlength += Arguments[i].size()+1;

	args->length = stringlength;
 	SmartBuf buffer = smartAnyAlloc(args->length);
	if (!buffer) return -2;

	args->commandLine = (char *)buffer.get();
	memset(args->commandLine, '\0', args->length);

	u32 argc = 0;
	u32 position = 0;

	/** Append Arguments **/
	for(u32 i = 0; i < Arguments.size(); i++)
	{
		strcpy(&args->commandLine[position], Arguments[i].c_str());
		position += Arguments[i].size() + 1;
		argc++;
	}

	args->argc = argc;
	args->argv = &args->commandLine;
	args->endARGV = args->argv + 1;

	Arguments.clear();

	return 0;
}

int BootHomebrew()
{
	if(homebrewsize == 0) return -1;

	struct __argv args;
	SetupARGV(&args);

	u32 cpu_isr;

	//entrypoint entry = (entrypoint) load_dol_image(homebrewbuffer, &args);
	entrypoint entry = (entrypoint) load_dol_image(app_booter_dol, &args);
	if (!entry) return -1;

	SYS_ResetSystem(SYS_SHUTDOWN, 0, 0);
	_CPU_ISR_Disable (cpu_isr);
	__exception_closeall();
	entry();
	_CPU_ISR_Restore (cpu_isr);

	return 0;
}
